/**
 * Copyright(c) 2014 Xunton Solutions
 *
 * History:
 *   15-6-9 上午9:09 Created by dwzhang
 */
package com.xunton.bss.weixin.api.component;

import java.util.HashMap;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Named;

import com.jade.framework.base.util.HttpUtils;
import com.jade.framework.cache.CacheService;
import com.jade.framework.cache.CacheUtils;
import com.xunton.bss.weixin.api.Response;
import com.xunton.bss.weixin.api.WeixinException;
import com.xunton.bss.weixin.api.component.pdu.AuthorizerInfo;
import com.xunton.bss.weixin.api.component.response.*;
import org.apache.commons.lang3.StringUtils;

/**
 * 微信第三方平台的开发接口。
 *
 * @author <a href="mailto:dwzhang@xunton.com">dwzhang</a> 
 * @version 1.0 15-6-9 上午9:09
 */
public class ComponentAgent
{
    private static final String URL_COMPONENT = "https://api.weixin.qq.com/cgi-bin/component/";
    private static final String URL_ACCESS_TOKEN = URL_COMPONENT + "api_component_token";
    private static final String URL_PRE_AUTH_CODE = URL_COMPONENT + "api_create_preauthcode";
    private static final String URL_QUERY_AUTH = URL_COMPONENT + "api_query_auth";
    private static final String URL_REFRESH_TOKEN = URL_COMPONENT + "api_authorizer_token";
    private static final String URL_GET_AUTHINFO = URL_COMPONENT + "api_get_authorizer_info";
    private static final String URL_GET_OPTION = URL_COMPONENT + "api_get_authorizer_option";
    private static final String URL_SET_OPTION = URL_COMPONENT + "api_set_authorizer_option";
    private static String PREFIX_CACHE_COMPONENT = "bss.weixin.component";
    private CacheService cacheService;

    @Inject
    public void setCacheService(@Named ("system_cacheService") CacheService cacheService)
    {
        this.cacheService = cacheService;
    }

    /**
     * 获取第三方平台的Access Token，并把它保存在Cache中。
     *
     * @param appId 第三方平台appid
     * @param appSecret 第三方平台Secret
     * @return 第三方平台的Access Token
     */
    public String getAccessToken(String appId, String appSecret)
            throws WeixinException
    {
        //check if in cache
        String key = CacheUtils.joinKey(PREFIX_CACHE_COMPONENT, "token", appId);
        String token = cacheService.get(key);
        if (StringUtils.isNotBlank(token)) {
            return token;
        }
        String ticket = getVerifyTicket(appId);
        if (StringUtils.isBlank(ticket)) {
            throw new WeixinException("err.bss.weixin.component.invalid_ticket", "no ticket");
        }
        //invoke weixin interface
        Map<String, String> params = new HashMap<String, String>();
        params.put("component_appid", appId);
        params.put("component_appsecret", appSecret);
        params.put("component_verify_ticket", ticket);

        try {
            AccessTokenResponse response = HttpUtils.postJsonHttp(URL_ACCESS_TOKEN, params, AccessTokenResponse.class);
            if (response.isOK()) {
                //set to cache
                cacheService.set(key, response.getAccessToken(), response.getExpiresIn());
                return response.getAccessToken();
            }
            else {
                throw handleErrorResposne(response);
            }
        }
        catch (Exception e) {
            throw handleException(e);
        }
    }

    protected WeixinException handleErrorResposne(Response response)
            throws WeixinException
    {
        return new WeixinException("err.bss.weixin.api.component.server",
                response.getErrorCode() + ":" + response.getErrorMsg());
    }

    protected WeixinException handleException(Exception e)
            throws WeixinException
    {
        if (e instanceof WeixinException) {
            return (WeixinException) e;
        }
        else {
            return new WeixinException("err.bss.weixin.api.component.client", e.getMessage(), e);
        }
    }

    /**
     * 获取预授权码。
     *
     * @param componentAppId 第三方平台appid
     * @param accessToken 访问令牌
     * @return 预授权码
     */
    public String getPreAuthCode(String componentAppId, String accessToken)
            throws WeixinException
    {
        //invoke weixin interface
        Map<String, String> params = new HashMap<String, String>();
        params.put("component_appid", componentAppId);
        try {
            PreAuthCodeResponse response = HttpUtils.postJsonHttp(
                    URL_PRE_AUTH_CODE + "?component_access_token=" + accessToken, params, PreAuthCodeResponse.class);
            if (response.isOK()) {
                return response.getPreAuthCode();
            }
            else {
                throw handleErrorResposne(response);
            }
        }
        catch (Exception e) {
            throw handleException(e);
        }
    }

    public AuthorizerInfo queryAuth(String componentAppId, String accessToken, String authCode)
            throws WeixinException
    {
        //invoke weixin interface
        Map<String, String> params = new HashMap<String, String>();
        params.put("component_appid", componentAppId);
        params.put("authorization_code", authCode);
        try {
            AuthorizerInfoResponse response = HttpUtils.postJsonHttp(
                    URL_QUERY_AUTH + "?component_access_token=" + accessToken, params, AuthorizerInfoResponse.class);
            if (response.isOK()) {
                return response.getInfo();
            }
            else {
                throw handleErrorResposne(response);
            }
        }
        catch (Exception e) {
            throw handleException(e);
        }
    }

    public AuthorizerInfo getAuthorizerInfo(String componentAppId, String accessToken, String authorizerAppId)
            throws WeixinException
    {
        //invoke weixin interface
        Map<String, String> params = new HashMap<String, String>();
        params.put("component_appid", componentAppId);
        params.put("authorizer_appid", authorizerAppId);
        try {
            AuthorizerInfoResponse response = HttpUtils.postJsonHttp(
                    URL_GET_AUTHINFO + "?component_access_token=" + accessToken, params, AuthorizerInfoResponse.class);
            if (response.isOK()) {
                return response.getInfo();
            }
            else {
                throw handleErrorResposne(response);
            }
        }
        catch (Exception e) {
            throw handleException(e);
        }
    }

    public String refreshAuthToken(String componentAppId, String accessToken, String authorizerAppId,
                                   String refreshToken)
            throws WeixinException
    {
        //invoke weixin interface
        Map<String, String> params = new HashMap<String, String>();
        params.put("component_appid", componentAppId);
        params.put("authorizer_appid", authorizerAppId);
        params.put("authorizer_refresh_token", refreshToken);
        try {
            RefreshTokenResponse response = HttpUtils.postJsonHttp(
                    URL_REFRESH_TOKEN + "?component_access_token=" + accessToken, params, RefreshTokenResponse.class);
            if (response.isOK()) {
                return response.getAuthorizerAccessToken();
            }
            else {
                throw handleErrorResposne(response);
            }
        }
        catch (Exception e) {
            throw handleException(e);
        }
    }

    public String getOption(String componentAppId, String accessToken, String authorizerAppId, String optionName)
            throws WeixinException
    {
        //invoke weixin interface
        Map<String, String> params = new HashMap<String, String>();
        params.put("component_appid", componentAppId);
        params.put("authorizer_appid", authorizerAppId);
        params.put("option_name", optionName);
        try {
            GetOptionResponse response = HttpUtils.postJsonHttp(
                    URL_GET_OPTION + "?component_access_token=" + accessToken, params, GetOptionResponse.class);
            if (response.isOK()) {
                return response.getOptionValue();
            }
            else {
                throw handleErrorResposne(response);
            }
        }
        catch (Exception e) {
            throw handleException(e);
        }
    }

    public void setOption(String componentAppId, String accessToken, String authorizerAppId, String optionName,
                          String optionValue)
            throws WeixinException
    {
        //invoke weixin interface
        Map<String, String> params = new HashMap<String, String>();
        params.put("component_appid", componentAppId);
        params.put("authorizer_appid", authorizerAppId);
        params.put("option_name", optionName);
        params.put("option_value", optionValue);
        try {
            Response response = HttpUtils.postJsonHttp(
                    URL_SET_OPTION + "?component_access_token=" + accessToken, params, Response.class);
            if (!response.isOK()) {
                throw handleErrorResposne(response);
            }
        }
        catch (Exception e) {
            throw handleException(e);
        }
    }

    public void setVerifyTicket(String componentAppId, String ticket)
    {
        //put cache 中
        String key = CacheUtils.joinKey(PREFIX_CACHE_COMPONENT, "ticket", componentAppId);
        cacheService.set(key, ticket, 1200);
    }

    public String getVerifyTicket(String componentAppId)
    {
        String key = CacheUtils.joinKey(PREFIX_CACHE_COMPONENT, "ticket", componentAppId);
        return cacheService.get(key);
    }
}
